//
//  RealtimeVoiceServiceTests.swift
//  PeekabooTests
//

import Foundation
import PeekabooCore
import Tachikoma
import Testing
@testable import Peekaboo

@Suite("RealtimeVoiceService Tests", .tags(.unit, .ai), .disabled("Uses full PeekabooServices which may hang"))
@MainActor
struct RealtimeVoiceServiceTests {
    // MARK: - Test Helpers

    private func createMockDependencies() throws -> (PeekabooAgentService, SessionStore, PeekabooSettings) {
        let services = PeekabooServices()
        let agentService = try PeekabooAgentService(services: services)
        let sessionStore = SessionStore()
        let settings = PeekabooSettings()
        settings.connectServices(services)
        return (agentService, sessionStore, settings)
    }

    // MARK: - Initialization Tests

    @Test("Service initializes with correct dependencies")
    func serviceInitialization() throws {
        let (agentService, sessionStore, settings) = try try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        #expect(service.isConnected == false)
        #expect(service.connectionState == .idle)
        #expect(service.currentTranscript.isEmpty)
        #expect(service.conversationHistory.isEmpty)
        #expect(service.error == nil)
        #expect(service.isRecording == false)
        #expect(service.isSpeaking == false)
        #expect(service.audioLevel == 0.0)
        #expect(service.selectedVoice == .alloy)
    }

    @Test("Service loads voice preference from settings")
    func voicePreferenceLoading() throws {
        let (agentService, sessionStore, settings) = try try self.createMockDependencies()

        // Set a voice preference in settings
        settings.realtimeVoice = "echo"

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        #expect(service.selectedVoice == .echo)
    }

    // MARK: - Session Management Tests

    @Test("Starting session without API key fails", .tags(.integration))
    func startSessionWithoutAPIKey() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        // Ensure no API key is set
        settings.openAIAPIKey = ""
        TachikomaConfiguration.current.setAPIKey("", for: .openai)

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        await #expect(throws: (any Error).self) {
            try await service.startSession()
        }

        #expect(service.isConnected == false)
        #expect(service.error != nil)
    }

    @Test("Ending session cleans up properly")
    func endSessionCleanup() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        // Simulate a connected state
        await service.endSession()

        #expect(service.conversation == nil)
        #expect(service.isConnected == false)
        #expect(service.connectionState == .idle)
        #expect(service.isRecording == false)
        #expect(service.isSpeaking == false)
        #expect(service.audioLevel == 0.0)
    }

    // MARK: - Recording Tests

    @Test("Toggle recording requires active connection")
    func toggleRecordingWithoutConnection() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        await #expect(throws: RealtimeError.notConnected) {
            try await service.toggleRecording()
        }
    }

    // MARK: - Message Sending Tests

    @Test("Sending message requires active connection")
    func sendMessageWithoutConnection() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        await #expect(throws: RealtimeError.notConnected) {
            try await service.sendMessage("Test message")
        }
    }

    @Test("Sending message updates conversation history")
    func sendMessageUpdatesHistory() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        // Note: This would require mocking the conversation
        // For now, we just verify the method exists and can be called
        #expect(service.conversationHistory.isEmpty)
    }

    // MARK: - Voice Settings Tests

    @Test("Update voice setting persists to settings")
    func updateVoicePersistence() throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        service.updateVoice(.shimmer)

        #expect(service.selectedVoice == .shimmer)
        #expect(settings.realtimeVoice == "shimmer")
    }

    // MARK: - Interrupt Tests

    @Test("Interrupt requires active connection")
    func interruptWithoutConnection() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        await #expect(throws: RealtimeError.notConnected) {
            try await service.interrupt()
        }
    }

    // MARK: - Error Handling Tests

    @Test("Service handles connection failures gracefully")
    func connectionFailureHandling() async throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        // Set an invalid API key to trigger failure
        settings.openAIAPIKey = "invalid-key"
        TachikomaConfiguration.current.setAPIKey("invalid-key", for: .openai)

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        do {
            try await service.startSession()
            Issue.record("Expected connection to fail with invalid API key")
        } catch {
            #expect(service.error != nil)
            #expect(service.isConnected == false)
        }
    }

    // MARK: - State Management Tests

    @Test("Connection states transition correctly")
    func connectionStateTransitions() throws {
        let (agentService, sessionStore, settings) = try self.createMockDependencies()

        let service = RealtimeVoiceService(
            agentService: agentService,
            sessionStore: sessionStore,
            settings: settings)

        // Initial state
        #expect(service.connectionState == .idle)

        // Other state transitions would require mocking the conversation
    }
}

// MARK: - Test Tags

// Tags are already defined in TestTags.swift
